<?php

namespace app\api\service;

use app\lib\exception\ParameterException;
use think\Cache;
use think\Log;
use Wxpay\WxPayApi;
use Wxpay\WxPayBizPayUrl;
use Wxpay\WxPayJsApiPay;
use Wxpay\WxPayRefund;
use Wxpay\WxPayUnifiedOrder;

class WxpayService
{
    /**
     * WxpayService constructor.
     */
    public function __construct()
    {
    }

    /**
     * 小程序-支付
     * modify by wind.
     *
     * @param $payinfo
     *
     * @throws ParameterException
     *
     * @return array|string|Url
     */
    public function xcxCheckValid($payinfo)
    {
        $data = [
            'appid' => 'wx730150b8ee9e9061',
            'sub_appid' => 'wx1bdacbd6dc02aa3c', //校务帮
            'mch_id' => '1530005791', //服务商
            'sub_mch_id' => '1559160511', //子商户 岂不快哉云课堂
            'key' => 'BIMZTSQC360Z9idylg0NlQmA3j6ZmRAa',
            'trade_type' => 'JSAPI',
            'bodys' => $payinfo['bodys'],
            'attach' => $payinfo['attach'],
            'notify_url' => $payinfo['notify'],
            'total_fee' => $payinfo['totalprice'],
            'order_code' => $payinfo['order_code'],
            'openid' => $payinfo['userid'],
        ];
        $result = [];
		$result['config'] = $this->getWxConfig();
		$result['sj'] = $this->xcxjsApiPay($data);
        if (!$result) {
            throw new ParameterException(['msg' => '支付信息不存在', 'error_code' => 8000]);
        }

        return $result;
    }

	/**
	 * 微信扫码支付
	 * User: wufeng
	 * Date: 2020/04/25
	 *
	 * @param $data
	 * @return mixed|\Wxpay\成功时返回，其他抛异常
	 * @throws ParameterException
	 * @throws \think\Exception
	 */
    public function wxsmPay($data)
    {
//		$mch = (new MerchantModel())->get($data['mch_id']);
		$attach = $notify_url = '';
		if(1 === (int) $data['type']){
			$attach = $data['user_id'];
			$notify_url = 'https://'.$_SERVER['HTTP_HOST'].'/api/notify/czwxsmNotify';
		}elseif (2 === (int) $data['type']){
			$attach = $data['user_id'].','.$data['checked_ids'];
			$notify_url = 'https://'.$_SERVER['HTTP_HOST'].'/api/notify/ddwxsmNotify';
		}

        $data = [
            'appid' => 'wx730150b8ee9e9061',
            'sub_appid' => 'wx1bdacbd6dc02aa3c', //校务帮
            'mch_id' => '1530005791', //服务商
            'sub_mch_id' => '1559160511', //子商户 岂不快哉云课堂
            'key' => 'BIMZTSQC360Z9idylg0NlQmA3j6ZmRAa',
            'trade_type' => 'NATIVE',
            'bodys' => '校务帮',
            'attach' => $attach,
            'notify_url' => $notify_url,
            'total_fee' => $data['amount'] * 100,
            'order_code' => $data['receipt_number'],
//            'openid' => $data['openid'],
            'product_id' => $data['receipt_number'],
        ];
		$result = $this->wxNativePay($data);
        if (!$result) {
            throw new ParameterException(['msg' => '支付信息不存在', 'error_code' => 8000]);
        }

        return $result;
    }

	/**
	 * User: wufeng
	 * Date: 2020/04/25
	 *
	 * @param $params
	 * @param $type
	 * @return mixed|\Wxpay\成功时返回，其他抛异常
	 * @throws ParameterException
	 * @throws \think\Exception
	 */
    public function xcxsmPay($params,$type)
    {
		$attach = $notify_url = '';
    	if(1 === (int) $type){
			$attach = $params['user_id'];
			$notify_url = 'https://'.$_SERVER['HTTP_HOST'].'/api/notify/czxcxsmNotify';
		}elseif (2 === (int) $type){
			$arr = array(
				'user_id' => $params['user_id'],
				'eduadmin_id' => $params['eduadmin_id'],
				'checked_ids' => $params['checked_ids']
			);
			$attach = implode(',',$arr);
			$notify_url = 'https://'.$_SERVER['HTTP_HOST'].'/api/notify/ddxcxsmNotify';
		}

//		$mch = (new MerchantModel())->get($data['mch_id']);
        $data = [
            'appid' => 'wx730150b8ee9e9061',
            'sub_appid' => 'wx1bdacbd6dc02aa3c', //校务帮
            'mch_id' => '1530005791', //服务商
            'sub_mch_id' => '1559160511', //子商户 岂不快哉云课堂
            'key' => 'BIMZTSQC360Z9idylg0NlQmA3j6ZmRAa',
            'trade_type' => 'NATIVE',
            'bodys' => '校务帮',
            'attach' => $attach,
            'notify_url' => $notify_url,
            'total_fee' => $params['amountcollected'] * 100,
            'order_code' => $params['receipt_number'],
//            'openid' => $data['openid'],
            'product_id' => $params['receipt_number'],
        ];
		$result = $this->wxNativePay($data);
        if (!$result) {
            throw new ParameterException(['msg' => '支付信息不存在', 'error_code' => 8000]);
        }

        return $result;
    }

	/**
	 * User: wufeng
	 * Date: 2020/04/08
	 *
	 * @return mixed
	 */
    public function getWxConfig()
    {
        $appid = 'wx1bdacbd6dc02aa3c';
        $secret = 'e90f2e7033c4abd86b2db942db23d466';
        $data['noncestr'] = time();
        $data['jsapi_ticket'] = $this->getTicket($appid, $secret);
        $data['timestamp'] = time();
        $data['url'] = request()->url(true);
        ksort($data);
        $val = $this->ToUrlParams($data);
        $data['signature'] = sha1($val);

        return $data;
    }

	/**
	 * User: wufeng
	 * Date: 2020/04/08
	 *
	 * @param $appid
	 * @param $secret
	 * @return mixed
	 */
    public function getTicket($appid, $secret)
    {
        Log::record(' 1、是否进入这里？ ', 'wx');
        if (Cache::get($appid.'_jt')) {
            Log::record(' 2、是否进入这里？ ', 'wx');

            return Cache::get($appid.'_jt');
        }
        $result = $this->getAccessToken($appid, $secret);
        $url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='.$result.'&type=jsapi';
        $sj = file_get_contents($url);
        $sj = json_decode($sj, 1);
        Log::record(json_encode($sj), 'wx');
        Log::record(' 3、是否进入这里？ ', 'wx');
        Cache::set($appid.'_jt', $sj['ticket'], 7000);

        return $sj['ticket'];
    }

    /**
     * 获取access_token.
     *
     * @param $appid
     * @param $secret
     *
     * @return mixed
     */
    public function getAccessToken($appid, $secret)
    {
        Log::record('4、是否进入这里？ ', 'wx');
        if (Cache::get($appid.'_at')) {
            Log::record(' 5、是否进入这里？ ', 'wx');

            return Cache::get($appid.'_at');
        }
        $url = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='.$appid.'&secret='.$secret;
        $sj = file_get_contents($url);
        $sj = json_decode($sj, 1);
        Log::record('token==========='.json_encode($sj), 'wx');
        Log::record('token==========='.json_encode($sj), 'error');
        Log::record(' 6、是否进入这里？ ', 'wx');
        Cache::set($appid.'_at', $sj['access_token'], 7000);

        return $sj['access_token'];
    }

	/**
	 * JSAPI支付
	 * User: wufeng
	 * Date: 2020/04/16
	 *
	 * @param $data
	 * @return mixed|\Wxpay\成功时返回，其他抛异常
	 * @throws \think\Exception
	 */
    public function xcxjsApiPay($data)
    {
        Log::record('打印'.json_encode($data), 'wx');
        $pay = new WxPayUnifiedOrder();
        $pay->SetAppid($data['appid']);     // 微信分配的公众账号ID
        $pay->SetMch_id($data['mch_id']);  // 微信支付分配的商户号
        $pay->SetSub_appid($data['sub_appid']);  // 当前调起支付的小程序APPID
        $pay->SetKey($data['key']);  // 签名，详见签名生成算法
        $pay->SetBody('商品费用');  // 腾讯充值中心-QQ会员充值 商品简单描述，该字段须严格按照规范传递，具体请见参数规定
        $pay->SetNotify_url($data['notify_url']);  // http://www.weixin.qq.com/wxpay/pay.php 接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数
        $pay->SetSub_openid($data['openid']);  // 用户Openid
        $pay->SetOut_trade_no($data['order_code']);  // 商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
        $pay->SetTotal_fee($data['total_fee']);  // 订单总金额，只能为整数，详见支付金额
        $pay->SetTrade_type($data['trade_type']); // JSAPI 小程序取值如下：JSAPI，详细说明见参数规定
        if (\array_key_exists('sub_mch_id', $data)) {
            $pay->SetSubMch_id($data['sub_mch_id']);  // 微信支付分配的子商户号
        }
        $pay->SetAttach($data['attach']); // 附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
        return $this->getXcxJsApiPaySign($pay);
    }

	/**
	 * NATIVE支付
	 * User: wufeng
	 * Date: 2020/04/16
	 *
	 * @param $data
	 * @return mixed|\Wxpay\成功时返回，其他抛异常
	 * @throws \think\Exception
	 */
    public function wxNativePay($data)
    {
		$pay = new WxPayUnifiedOrder();
		$pay->SetAppid($data['appid']);     // 微信分配的公众账号ID
		$pay->SetMch_id($data['mch_id']);  // 微信支付分配的商户号
		$pay->SetSub_appid($data['sub_appid']);  // 当前调起支付的小程序APPID
		$pay->SetKey($data['key']);  // 签名，详见签名生成算法
		$pay->SetBody('商品费用');  // 腾讯充值中心-QQ会员充值 商品简单描述，该字段须严格按照规范传递，具体请见参数规定
		$pay->SetNotify_url($data['notify_url']);  // http://www.weixin.qq.com/wxpay/pay.php 接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数
//		$pay->SetSub_openid($data['openid']);  // 用户Openid
		$pay->SetProduct_id($data['product_id']);  // trade_type=NATIVE时，此参数必传。此id为二维码中包含的商品ID，商户自行定义。
		$pay->SetOut_trade_no($data['order_code']);  // 商户系统内部订单号，要求32个字符内，只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
		$pay->SetTotal_fee($data['total_fee']);  // 订单总金额，只能为整数，详见支付金额
		$pay->SetTrade_type($data['trade_type']); // JSAPI 小程序取值如下：JSAPI，详细说明见参数规定
		if (\array_key_exists('sub_mch_id', $data)) {
			$pay->SetSubMch_id($data['sub_mch_id']);  // 微信支付分配的子商户号
		}
		$pay->SetAttach($data['attach']); // 附加数据，在查询API和支付通知中原样返回，该字段主要用于商户携带订单的自定义数据
        return $this->getWxNativePaySign($pay);
    }

	/**
	 * 微信退款
	 * User: wufeng
	 * Date: 2020/04/08
	 *
	 * @param $data
	 * @return mixed 成功时返回，其他抛异常
	 */
    public function wxRefund($data)
    {
        $pay = new WxPayRefund();
        $pay->SetAppid($data['appid']);             // 支付微信平台
        $pay->SetMch_id($data['mch_id']);           // 支付商户
        $pay->SetSub_Appid($data['sub_appid']);      // 支付子商户
        $pay->SetOut_trade_no($data['out_trade_no']);     // 支付单号
        $pay->SetOut_refund_no($data['out_refund_no']);    // 退款单号
        $pay->SetTotal_fee($data['total_fee']);        // 订单总金额
        $pay->SetRefund_fee($data['refund_fee']);       // 退款金额
        $pay->SetOp_user_id($data['userid']);
        $pay->SetKey($data['key']);       // key
        if (\array_key_exists('sub_mch_id', $data)) {
            $pay->SetSubMch_id($data['sub_mch_id']);           // 支付商户
        }
        $wxOrder = WxPayApi::refund($pay, $data['cert'], $data['key'], $data['mchkey']);
        if ('SUCCESS' !== $wxOrder['return_code'] || 'SUCCESS' !== $wxOrder['result_code']) {
            Log::record($wxOrder, 'error');
            Log::record('订单退款失败', 'error');
        }

        return $wxOrder;
    }

	/**
	 * User: wufeng
	 * Date: 2020/04/16
	 *
	 * @param $pay
	 * @return mixed|\Wxpay\成功时返回，其他抛异常
	 * @throws \think\Exception
	 */
    private function getXcxJsApiPaySign($pay)
    {
        $wxOrder = WxPayApi::unifiedOrder($pay);
        Log::record('打印'.json_encode($wxOrder), 'wx');
        if ('SUCCESS' !== $wxOrder['return_code'] || 'SUCCESS' !== $wxOrder['result_code']) {
            Log::record($wxOrder, 'error');
            Log::record('获取预支付订单失败', 'error');

            return $wxOrder;
        }
        $this->recordPreOrder($wxOrder);

        return $this->xcxsign($wxOrder);
    }

	/**
	 * 生成直接支付url，支付url有效期为2小时,模式二
	 * User: wufeng
	 * Date: 2020/04/16
	 *
	 * @param $pay
	 * @return \Wxpay\成功时返回，其他抛异常
	 * @throws \think\Exception
	 */
	private function getWxNativePaySign($pay)
	{
		try{
			$result = WxPayApi::unifiedOrder($pay);
			return $result;
		} catch(Exception $e) {
			Log::ERROR(json_encode($e));
		}
	}

	/**
	 * User: wufeng
	 * Date: 2020/04/08
	 *
	 * @return bool
	 */
    private function recordPreOrder()
    {
        return true;
    }

	/**
	 * User: wufeng
	 * Date: 2020/04/08
	 *
	 * @param $pay
	 * @return mixed
	 */
    private function xcxsign($pay)
    {
        $sign = new WxPayJsApiPay();
        $sign->setAppid($pay['sub_appid']);
        $timeStamp = time().'';
        $sign->SetTimeStamp($timeStamp);
        $sign->SetNonceStr(md5(time().mt_rand(0, 1000)));
        $sign->SetPackage('prepay_id='.$pay['prepay_id']);
        $sign->SetSignType('md5');
        $s = $this->MakeSign($sign);
        $sign->SetPaySign($s);

        return $sign->GetValues();
    }

	/**
	 * User: wufeng
	 * Date: 2020/04/08
	 *
	 * @param $sign
	 * @return string
	 */
    private function MakeSign($sign)
    {
		$key = 'BIMZTSQC360Z9idylg0NlQmA3j6ZmRAa';
        $val = $sign->GetValues();
        //签名步骤一：按字典序排序参数
        ksort($val);
        $string = $this->ToUrlParams($val);
        //签名步骤二：在string后加入KEY
        $string = $string.'&key='.$key;
        Log::record(' key '.json_encode($key), 'wx');
        //签名步骤三：MD5加密
        Log::record(' 签名排序1 '.json_encode($string), 'wx');
        $str = md5($string);
        Log::record(' 签名排序2 '.json_encode($str), 'wx');
        //签名步骤四：所有字符转为大写
        $result = strtoupper($str);
        Log::record(' 最终签名 '.json_encode($result), 'wx');

        return $result;
    }

	/**
	 * 格式化参数格式化成url参数
	 * User: wufeng
	 * Date: 2020/04/08
	 *
	 * @param $val
	 * @return string
	 */
    private function ToUrlParams($val)
    {
        $buff = '';
        foreach ($val as $k => $v) {
            if ('sign' !== $k && '' !== $v && !\is_array($v)) {
                $buff .= $k.'='.$v.'&';
            }
        }

        return trim($buff, '&');
    }
}
